﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace UmcProvider.Utils
{
    internal static class Reflection
    {
        public static IEnumerable<String> GetPropertieNames<T>()
        {
            List<String> props = new List<String>();
            Type t = typeof(T);
            t.GetInterfaces().Concat(new Type[] { t }).All(p =>
            {
                props.AddRange(p.GetProperties().Select(q => q.Name));
                return true;
            });
            return props.Distinct().ToArray();
        }


        public static void RemoveNonUmcProperty<T>(IDictionary properties, List<String> exceptionList)
            where T : IUmc
        {
            var excludedProperties = properties.Keys.Cast<String>().
                Except(exceptionList).
                Except(Reflection.GetPropertieNames<T>()).
                ToArray();

            foreach (string propertyName in excludedProperties)
            {
                properties.Remove(propertyName);
            }
        }

        public static IEnumerable<String> GetPropertieNames(IEnumerable<Type> typeEnumer)
        {
            List<String> props = new List<String>();

            foreach (Type eachType in typeEnumer)
                props.AddRange(eachType.GetProperties().Select(q => q.Name));

            return props.Distinct().ToArray();
        }

        public static IEnumerable<Type> GetInterfacesIncludeSelf(Type t)
        {
            Type[] allInterface = null;

            if (t.IsInterface)
                allInterface = t.GetInterfaces().Concat(new Type[] { t }).ToArray();
            else
                allInterface = t.GetInterfaces();

            return allInterface;
        }

        private static void IncludeProperty<T>(IDictionary properties, List<String> exceptionList)
        {
            var excludedProperties = properties.Keys.Cast<String>().
                Except(exceptionList).
                Except(Reflection.GetPropertieNames<T>()).
                ToArray();

            foreach (string propertyName in excludedProperties)
            {
                properties.Remove(propertyName);
            }
        }

        private static void ExceptProperty<T>(IDictionary properties, List<String> excludedList)
        {
            IEnumerable<Type> excludeInterfaces = null;
            IEnumerable<String> excludedProperties = null;

            excludeInterfaces = GetInterfacesIncludeSelf(typeof(T)).
                Where(t => t.IsDefined(typeof(ExcludePropertyAttribute), false));
            excludedProperties = GetPropertieNames(excludeInterfaces).
                Concat(excludedList);

            foreach (string propertyName in excludedProperties)
            {
                // Remove will do nothing if specified 'propertyName' is not included in 'properties'.
                properties.Remove(propertyName);
            }
        }

        public static void RemoveNonUmcProperty<T>(IDictionary properties, List<String> exceptionList, List<String> excludedList)
            where T : IUmc
        {
            Type firstExcludeInterface = null;

            firstExcludeInterface = Reflection.GetInterfacesIncludeSelf(typeof(T)).
                FirstOrDefault(t => t.IsDefined(typeof(ExcludePropertyAttribute), false));

            if (null != firstExcludeInterface)
                ExceptProperty<T>(properties, excludedList);
            else
                IncludeProperty<T>(properties, exceptionList);
        }
    }
}
